using Fusion.Frameworks.Assets;
using Fusion.Utilities;
using Fusion.Utilities.Generics;
using Fusion.Utilities.Singleton;
using System.Collections.Generic;
using UnityEngine;

namespace Fusion.Frameworks.PoolManager
{

    public class GameObjectPool : SingletonBehaviour<GameObjectPool>
    {
        OrderDictionary<string, List<GameObject>> storages = new OrderDictionary<string, List<GameObject>>();

        private string GetMark(string path)
        {
            return path.Replace("/", "_");
        }

        public GameObject Get(string path, GameObject parent = null)
        {
            GameObject recycleObject = null;
            string mark = GetMark(path);
            if (!storages.ContainsKey(mark))
            {
                storages[mark] = new List<GameObject>();
                GameObject storageRoot = new GameObject(mark);
                storageRoot.transform.SetParent(transform);
                storageRoot.transform.localPosition = Vector3.zero;
            } 

            List<GameObject> list = storages[mark];
            for (int index = list.Count - 1; index >= 0; index--)
            {
                if (list[index] != null)
                {
                    PoolMark poolMark = list[index].GetComponent<PoolMark>();
                    if (!poolMark.IsUsing && recycleObject == null)
                    {
                        recycleObject = list[index];
                        poolMark.IsUsing = true;
                    }
                }
                else
                {
                    list.RemoveAt(index);
                }
                
            }
            bool isNew = false;
            if (recycleObject == null)
            {
                recycleObject = AssetsUtility.CreateGameObject(path, Utility.Find(gameObject, mark));
                PoolMark poolMark = recycleObject.GetOrAddComponent<PoolMark>();
                poolMark.Mark = mark;
                poolMark.Index = list.Count;
                poolMark.IsUsing = true;
                isNew = true;
                list.Add(recycleObject);
            }

            if (parent != null)
            {
                recycleObject.transform.SetParent(parent.transform);
                recycleObject.transform.localPosition = Vector3.zero;
                recycleObject.transform.localScale = Vector3.one;
            }

            PoolLifeCycle poolLifeCycle = recycleObject.GetComponent<PoolLifeCycle>();
            recycleObject.SetActive(true);
            if (poolLifeCycle != null)
            {
                if (isNew) 
                {
                    poolLifeCycle.OnCreate();
                }

                poolLifeCycle.Init();
            }
            return recycleObject;
        }

        public void Recycle(GameObject recycleObject)
        {
            PoolLifeCycle poolLifeCycle = recycleObject.GetComponent<PoolLifeCycle>();
            if (poolLifeCycle != null)
            {
                poolLifeCycle.OnRecycle();
            }

            PoolMark poolMark = recycleObject.GetComponent<PoolMark>();
            poolMark.IsUsing = false;
            recycleObject.transform.SetParent(Utility.Find(gameObject, poolMark.Mark).transform);
            recycleObject.SetActive(false);
        }

        public void Recycle(string path)
        {
            string mark = GetMark(path);
            if (storages.ContainsKey(mark))
            {
                List<GameObject> list = storages[mark];
                for (int i = 0; i < list.Count; i++)
                {
                    GameObject recycleObject = list[i];
                    PoolMark poolMark = recycleObject.GetComponent<PoolMark>();
                    if (poolMark.IsUsing)
                    {
                        Recycle(recycleObject);
                    }
                }
            }
        }

        public void Recycle()
        {
            int count = storages.Count;
            for (int i = count - 1; i >= 0; i--)
            {
                Recycle(storages.GetKey(i));
            }
        }

        public void Clear()
        {
            int count = storages.Count;
            for (int i = count - 1; i >= 0; i--)
            {
                Clear(storages.GetKey(i));
            }
        }

        public void Clear(string path)
        {
            Recycle(path);
            string mark = GetMark(path);
            if (storages.ContainsKey(mark))
            {
                storages.Remove(mark);
                GameObject storageRoot = Utility.Find(gameObject, mark);
                AssetsUtility.Release(storageRoot);
            }
        }

        public override void Initializate()
        {
            base.Initializate();
            persistent = false;
        }
    }
}
